package com.databias.j15;

import java.awt.Color;
import java.awt.Font;
import java.awt.font.*;
import java.awt.Graphics;
import java.awt.geom.*;
import java.awt.Rectangle;
import java.awt.Graphics2D; //  used for draw function
import java.awt.Shape;
import java.awt.BasicStroke;
import java.awt.TexturePaint;
import java.awt.image.*;


public class GGauge implements GObject{

/**
 * Implement a gauge object (speedometer) suitable for display.  The object will use a percentage basis to 
 * display any value.
 * 
 */	
	private Rectangle myRect;
	private Rectangle myOutline;
	
	private int originx, originy;
	private int gaugeSize; // The width of our gauge
	
	private int currValue; // The value they've last set (percentage)
	private int currDegrees;
	
	// Set some display characteristics
	private boolean filled=true;
	private boolean solid=false;
	private boolean tics=true;
	private boolean showVal=false;
	
	// To give a fill, we'll create a small static image
	
	private static BufferedImage texImage = new BufferedImage(2, 2, BufferedImage.TYPE_INT_RGB);
	private static Rectangle texRect =  new Rectangle(0,0,2,2);
	
	// An arrow for the gas gauge
	private static GeneralPath arrow;
	
	// Create our texture (an image
	static {
		Graphics2D big = texImage.createGraphics();
		big.setColor(Color.BLACK);
		big.fillRect(0, 0, 2, 2);
		big.setColor(Color.WHITE);
		big.fillRect(0, 0, 1, 1);
		
		// Create an arrow for use
	      int xPoints[] = { 0 , 8, 8, 10, 8, 8 };
	      int yPoints[] = { 0, 0, 2, 0, -2, 0 }; 
	      
         arrow = new GeneralPath(); // create GeneralPath object
        
        // set the initial coordinate of the General Path
        arrow.moveTo( xPoints[ 0 ], yPoints[ 0 ] );       
       
        // create the star--this does not draw the arrow
        for ( int count = 1; count < xPoints.length; count++ )
        	arrow.lineTo( xPoints[ count ], yPoints[ count ] );
       
        arrow.closePath(); // close the shape
	}
	


	
	/**
	 * Set the percentage of graph filled
	 * @param v
	 */
	public void setValue(int v)
	{
		if (v > 100) v=100;
		if (v < 0) v=0;
		
		
		currDegrees = v*180/100;
		currValue=v;
	}
	
	public void setFilled(boolean b) {filled=b;};
	public void setSolid(boolean b) {solid=b;};
	public void setTics(boolean b) {tics=b;};
	public void showText(boolean b) {showVal=b;};
	
	/**
	 * Draw a tic mark
	 * @param g
	 * @param where
	 * @param offset
	 */
	private void drawTick(Graphics g, int x, int y, double ticlen, double deg) {

		

		// Let's try to remember our trig
		// We want a point D degrees from our origin at radius R
		// the x value should be the cosine of the angle times the radius
		int dx1 = (int)Math.rint((gaugeSize/2-ticlen) * Math.cos(Math.toRadians((deg))));
		int dy1 = (int)Math.rint((gaugeSize/2-ticlen)  * Math.sin(Math.toRadians((deg))));
		
		// Now to the outside
		int dx2 = (int)Math.rint((gaugeSize/2+ticlen) * Math.cos(Math.toRadians((deg ))));
		int dy2 = (int)Math.rint((gaugeSize/2+ticlen)  * Math.sin(Math.toRadians((deg))));
		

		g.drawLine(dx1+x+gaugeSize/2,dy1+y+gaugeSize/2,dx2+x+gaugeSize/2,dy2+y+gaugeSize/2);
	};
	
	
	/** 
	 * Version with no initial set value, chain to the version which does.
	 * 
	 * @param size
	 */
	public GGauge (int size)
	{
		this(size,0);
	}
	
	/**
	 * 
	 * @param size The width of the gauge (the height is 1/2 width)
	 * @param value Initial value (percentage)
	 */
	public GGauge (int size, int value)
	{	
		
		// What we mostly care about is how many degrees it is.
		setValue(value);
		
		
		if (size <0) size=0;
		gaugeSize=size;

	}
	

	
	/**
	 * 
	 * Ideas taken from: http://forum.java.sun.com/thread.jspa?threadID=524360&messageID=2752300
	 * and :http://www.adtmag.com/java/articleold.aspx?id=1241
	 * and http://biptest.weizmann.ac.il/course/prog2/tutorial/2d/display/strokeandfill.html
	 * 
	 * @author Frederick
	 *
	 */
/*	public class Pointer 
	{
		final int HEADSIZE=3;
		GeneralPath oddShape = new GeneralPath();
		
		public Pointer(int len)
		{
			// Create an arrow shape, consisting of a line and a triangle at the end of it
			// It should be such that the point is at the end of the line
			//  (0,0) -----> (gs/2,0)
			
//			Shape s0 = new Line2D.Double(0, 0,  len, 0);
//			
//			// Let's do a bit of math.  We know that from the 'tip', we want to come back 45 degrees
//			// let's pick an arbitrary 3 pixels (as a const)
//			
//			Shape s0 = new Line2D.Double(0, 0, gaugeSize/2, 0);
//			Shape s1 = new Line2D.Double(gaugeSize/2, 0 , (gaugeSize/2)-HEADSIZE, -(HEADSIZE/2));
//			Shape s2 = new Line2D.Double(gaugeSize/2, 0 , (gaugeSize/2)-HEADSIZE, (HEADSIZE/2));
			// Stopping that way for now, looking at greating a general path
			
		      int xPoints[] = { 0 , 8, 8, 10, 8, 8 };
		      int yPoints[] = { 0, 0, 2, 0, -2, 0 }; 
		        
		        Graphics2D g2d = ( Graphics2D ) g;
		        GeneralPath arrow = new GeneralPath(); // create GeneralPath object
		       
		        // set the initial coordinate of the General Path
		        arrow.moveTo( xPoints[ 0 ], yPoints[ 0 ] );       
		       
		        // create the star--this does not draw the star
		        for ( int count = 1; count < xPoints.length; count++ )
		        	arrow.lineTo( xPoints[ count ], yPoints[ count ] );
		       
		        arrow.closePath(); // close the shape
//	        public GeneralPath createPath(int x, int y) {
//	    	    x2 = x;
//	    	    y2 = y;
//	                oddShape.moveTo(x, y);
//	    	    x -= 100;
//	    	    oddShape.lineTo(x, y);
//	    	    y += 50;
//	    	    oddShape.lineTo(x, y);
//	    	    x += 100;
//	    	    oddShape.lineTo(x, y);
//	                x += 10;
//	    	    y -= 10;
//	    	    x1 = x - 20;
//	    	    y1 = y - 20;
//	    	    oddShape.curveTo(x, y, x1, y1, x2, y2);
//	    	    return oddShape;
		}
	}*/

	/**
	 * x,y are passed to the draw method, we don't need to store it.
	 * 
	 * The arc will fit into a rectangle specified by its upper left corner of x,y
	 */
	public void draw(int x, int y, Graphics2D gs) {
		
		gs.setColor(Color.BLACK);
		gs.setStroke((new BasicStroke(1.0f)));
		
		// Draw the base
		gs.drawLine(x,y+(gaugeSize/2) , x+gaugeSize, y+(gaugeSize/2));
		
		if (filled) {
			if (solid) {
				gs.setPaint(Color.black);

			} else {
				// Use a fill texture (staticly created)
				gs.setPaint(new TexturePaint(texImage, texRect));
			}

			gs.fillArc(x, y, gaugeSize, gaugeSize, 180, -currDegrees);
		}
		else // Just a pointer 
		{
			gs.setColor(Color.BLACK);
			// Let's try to remember our trig
			// We want a point D degrees from our origin at radius R
			// the x value should be the cosine of the angle times the radius
			
			
			/*  This code works, but want to do it with an arrow
			// Now to the outside, draw a single line
			int dx2 = (int)Math.rint((gaugeSize/2) * Math.cos(Math.toRadians(currDegrees +180  )));
			int dy2 = (int)Math.rint((gaugeSize/2)  * Math.sin(Math.toRadians(currDegrees + 180 )));
			
			gs.drawLine(x+(gaugeSize/2),y+(gaugeSize/2),x+(gaugeSize/2)+dx2,y+dy2+(gaugeSize/2));
			
			*/
			
			/* This wacky version ends up rotating the entire screen!
	        gs.translate( x, y ); 
	        // rotate around origin and draw arrow

	           gs.rotate( Math.toRadians(currDegrees   ) ); // rotate coordinate system
	           gs.draw(arrow);
	           
 			*/
			
			// Okay, this version looks like I can use an Affine Transform and apply it to a single
			// shape
			// http://www.exampledepot.com/egs/java.awt.geom/TransformShape.html
			  AffineTransform tx = new AffineTransform();
			    
			  // Figure out our scale- it is the radius of the graph
			  // divided by the length of the Arrow
			  double halfScale=(gaugeSize/2);
			  halfScale=halfScale/(arrow.getBounds().width);
			  
			    //tx.shear(shiftx, shifty);
			  
			    tx.translate(x+(gaugeSize/2), y+(gaugeSize/2));
			    
			    tx.rotate(Math.toRadians(currDegrees +180   ));
			    // Scaling has to be done after any manipulation which has coordinates, otherwise
			    // the coordinates become scaled.
			    // The original arrow is 
			    tx.scale(halfScale,halfScale); //do the scaling after everything else
			    Shape newShape = tx.createTransformedShape(arrow);
			    //newShape.mo
			    gs.draw(newShape);
			    gs.fill(newShape);

			
		}
		
		// Draw the outside arc
		gs.setColor(Color.BLACK);
		gs.drawArc(x, y, gaugeSize, gaugeSize, 0, 180);  // starts at 3 o'clock and goes CC 180'
		
		if (tics) {
			// Tic marks
			for (double i = 0; i <= 180; i += 22.5) {
				drawTick(gs, x, y, 1.3, i + 180);
			} // little
			// Thicker
			gs.setStroke((new BasicStroke(2.0f)));
			for (double i = 0; i <= 180; i += 45) {
				drawTick(gs, x, y, 2, i + 180);
			} // big tics
		}
		
		//System.out.println(gs.getFont());
		// Show the numeric value?
		if (showVal)
		{
			// Create a text layout based on our text
			TextLayout textTl = new TextLayout(Integer.toString(currValue), 
					  new Font("Dialog", Font.BOLD, (gaugeSize/2)), 
					  new FontRenderContext(null, false, false));
			
			// Figure out where to put the text 'shape'
			AffineTransform textAt = new AffineTransform();
			
			// Determine the size of our new shape
			float gbx = (float)textTl.getBounds().getWidth();
			float gby = (float)textTl.getBounds().getHeight();
			
			// Center it in our graph
			textAt.translate((gaugeSize/2) -(gbx/2)+x,
					(gaugeSize/2) +y);
			//textAt.translate(x,y);
			Shape textShape = textTl.getOutline(textAt);
			
			// First fill it
			gs.setPaint(Color.black);
			gs.fill(textShape);
			
			// Now draw it (in white)
			gs.setStroke(new BasicStroke(0.0f));
			gs.setPaint(Color.white);
			gs.draw(textShape);
			
			//gs.drawString(Integer.toString(currValue), x, (int)(y-gaugeSize/2));
		}
		
//		// Old method
//		if (showVal)
//		{
//			gs.setFont( new Font ("Courier", Font.BOLD, (gaugeSize/2)));
//			gs.setStroke(new BasicStroke(2.0f));
//			gs.drawString(Integer.toString(currValue),100,40);
//			//gs.drawString(Integer.toString(currValue), x, (int)(y-gaugeSize/2));
//		}

	}

}
